#!/usr/bin/env python

from sqlalchemy.orm import scoped_session
from .exception import CustomException
from .utils import Utils
from .my_redis import MyRedis

import inspect
import time
from functools import wraps
from typing import Any, List
from core.my_log import logger

def fullname(o):
    module = inspect.getmodule(o)
    return f"{module.__name__}.{o.__qualname__}"

def apply(**kwargs):
    """ 注入数据库等资源 """
    def wapper(cls):
        for x in kwargs:
            setattr(cls, x, kwargs.get(x))
        return cls
    return wapper


# def apply(decorator: Any, exclude: List[str] = None):
#
#     if not exclude:
#         exclude = []
#     def decorate(cls):
#         for attr in cls.__dict__:
#             if callable(getattr(cls, attr)) and attr not in exclude:
#                 setattr(cls, attr, decorator(getattr(cls, attr)))
#         return cls
#     return decorate


def Singleton(cls):
    """ 单例 """
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton


def Transaction(name=None):
    """
    声明式事务,该方法只能使用在对方法上
    特性 :
        a. 支持直接传入session对象(暂无实现)
        b. 传入session对象对应的类书属性名称
    如果出现exception ,直接上抛异常给调用方
    """

    def wapper(func):
        def _deco(self, *args, **kwargs):
            if name is not None and hasattr(self, name):
                session = getattr(self, name)
                if isinstance(session, scoped_session):
                    try:
                        res = func(self, *args, **kwargs)
                        session.commit()
                        return res
                    except CustomException as ce:
                        session.rollback()
                        raise CustomException(code=ce.code, desc=ce.msg)
                    except Exception as e:
                        session.rollback()
                        raise e
                    finally:
                        pass
            else:
                print("找不到session对象，无法开启自动事务")

        return _deco

    return wapper


def Cache(name='redis', time=3000):
    """
    缓存
    """

    def outer(func):
        def _deco(self, *args, **kwargs):
            cache_key = Utils._compute_key(func, args, kwargs)
            if hasattr(self, name):
                cache_obj = getattr(self, name, None)
                if isinstance(cache_obj, MyRedis):
                    data = cache_obj.get(cache_key)
                    if data is None:
                        print("没有命中缓存,执行函数")
                        data = func(self, *args, **kwargs)
                        print("将数据加入到缓存中")
                        cache_obj.setex(cache_key, time, data)
                    else:
                        print("命中缓存")
                else:
                    data = func(self, *args, **kwargs)
            else:
                data = func(self, *args, **kwargs)

            return data

        return _deco

    return outer



def timer(func: Any):
    """Timing decorator that sends a timing metric."""

    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed_time = int((time.perf_counter()-start)*1000)
        logger.debug(f"function:{fullname(func)} elapsed.time:{elapsed_time}")
        return result
    return wrapper

